package net.greyar.theory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class JSON {
	private String name;
	private Map<String, Symbol> pattern;
	public static void main(String[] args) throws IOException, PatternException {
		JSON json = new JSON();
		String string = "{\"spiral\":[\"ache\",  -017.5,  [3,6],\"other\" ]}";
		System.out.println(string);
		int i = json.validate(string);
		System.out.println(i + " " + string.length());
		System.out.println(string);
	}
	public JSON() throws IOException {
		this.name = "json";
		this.pattern = loadMap(parseMany(Loader.pattern("json")));
	}
	private Map<String, Symbol> loadMap(List<Symbol> rules) {
		Map<String, Symbol> map = new HashMap<String, Symbol>();
		for (Symbol rule : rules) {
			map.put(rule.getKey(), rule);
		}
		return map;
	}
	public static List<Symbol> parseMany(String pat) {
		List<Symbol> all = new ArrayList<Symbol>();
		int i = 0;
		while (i < pat.length()) {
			char ch = pat.charAt(i);
			if (ch == '(') {
				Symbol one = new Expression();
				i = expression(pat, one, i + 1);
				one = one.level();
				one.toString(new StringBuilder());
				one.getPrint(new StringBuilder());
				all.add(one);
			} else {
				i++;
			}
		}
		return all;
	}
	private static int expression(String pat, Symbol parent, int i) {
		Symbol expression = new Expression();
		while (i < pat.length()) {
			char ch = pat.charAt(i);
			if (Character.isWhitespace(ch)) {
				i++;
			} else if (ch == '(') {
				i = expression(pat, expression, i + 1);
			} else if (ch == ')') {
				i++;
				break;
			} else if (ch == '\'') {
				i = quote(pat, expression, i);
			} else {
				i = literal(pat, expression, i);
			}
		}
		parent.add(expression);
		return i;
	}
	private static int quote(String pat, Symbol parent, int i) {
		int j = i + 1;
		while (j < pat.length() && pat.charAt(j) != '\'' && pat.charAt(j - 1) != '\\') {
			j++;
		}
		parent.add(new Quote(pat.substring(i, j + 1)));
		return j + 1;
	}
	private static int literal(String pat, Symbol parent, int i) {
		int j = i + 1;
		while (j < pat.length() && pat.charAt(j) != '(' && pat.charAt(j) != ')' && !Character.isWhitespace(pat.charAt(j))) {
			j++;
		}
		parent.add(new Literal(pat.substring(i, j)));
		return j;
	}
	public int validate(String string) throws PatternException {
		Symbol rule = pattern.get(this.name);
		return and(rule.getSymbols().subList(1, rule.getSymbols().size()), string, 0);
	}
	private int validate(Symbol symbol, String string, int pos) throws PatternException {
		Symbol rule = pattern.get(symbol.getKey());
		return and(rule.getSymbols().subList(1, rule.getSymbols().size()), string, pos);
	}
	private int and(List<Symbol> list, String string, int pos) throws PatternException {
		for (int i = 0; i < list.size(); i++) {
			pos = valid(list.get(i), string, pos);
		}
		return pos;
	}
	private int or(List<Symbol> list, String string, int pos) throws PatternException {
		for (int i = 0; i < list.size(); i++) {
			try {
				return valid(list.get(i), string, pos);
			} catch (PatternException e) {
				continue;
			}
		}
		throw new PatternException("or: " + string.charAt(pos) + " @" + pos + " meets none of " + list);
	}
	private int not(List<Symbol> list, String string, int pos) throws PatternException {
		try {
			for (int i = 0; i < list.size(); i++) {
				valid(list.get(i), string, pos);
			}
		} catch (PatternException e) {
			return ++pos;
		}
		throw new PatternException("not: " + string.charAt(pos) + " @" + pos + " shouldn't meet " + list);
	}
	private int opt(List<Symbol> list, String string, int pos) {
		try {
			return and(list, string, pos);
		} catch (PatternException e) {
		}
		return pos;
	}
	private int valid(Symbol symbol, String string, int pos) throws PatternException {
		if (symbol instanceof Literal) {
			if (pos < string.length()) {
				if (symbol.getToken().length() == 1) {
					if (string.charAt(pos) == symbol.getToken().charAt(0)) {
						return ++pos;
					} else {
						throw new PatternException("failed: " + string.charAt(pos) + " @" + pos + " != " + symbol);
					}
				} else if (symbol.getToken().equals("white")) {
					if (Character.isWhitespace(string.charAt(pos))) {
						return ++pos;
					} else {
						throw new PatternException("failed: " + string.charAt(pos) + " @" + pos + " != " + "whitespace");
					}
				} else {
					return validate(symbol, string, pos);
				}
			} else {
				throw new PatternException("failed: " + pos + " == " + string.length());
			}
		} else {
			if (symbol.getKey().equals("or")) {
				return or(symbol.getSymbols().subList(1, symbol.getSymbols().size()), string, pos);
			} else if (symbol.getKey().equals("opt")) {
				return opt(symbol.getSymbols().subList(1, symbol.getSymbols().size()), string, pos);
			} else if (symbol.getKey().equals("not")) {
				return not(symbol.getSymbols().subList(1, symbol.getSymbols().size()), string, pos);
			} else {
				return and(symbol.getSymbols(), string, pos);
			}
		}
	}
}
